
// ===============================================================================================================
// -*- C++ -*-
//
// MeshLib.hpp - Polygonal mesh representation.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __MESHLIB_HPP__
#define __MESHLIB_HPP__

#include <GfxLib.hpp>
#include <vector>
#include <map> // Use the std::map<> for groups and materials

namespace MeshLib {

// =========================================================
// Helper Structures
// =========================================================

// Material loading will fail if name is longer than this!
static const int materialNameMaxLen = 256;

enum PolyInfo {

	// Flags for the Triangles and Quads.
	POLY_HAS_VERTEX  = (1 << 1),
	POLY_HAS_NORMALS = (1 << 2),
	POLY_HAS_TEXTURE = (1 << 3),
};

struct Triangle {

	// Indices in the mesh arrays.
	unsigned int vertexIndex[3];
	unsigned int normalIndex[3];
	unsigned int texCoordIndex[3];

	// Data specified above is only valid if the propper flag is set.
	unsigned short flags;
};

struct Quad {

	// Indices in the mesh arrays.
	unsigned int vertexIndex[4];
	unsigned int normalIndex[4];
	unsigned int texCoordIndex[4];

	// Data specified above is only valid if the propper flag is set.
	unsigned short flags;
};

class Group {

public:

	std::vector<Triangle> trangles; // Triangle faces, if any.
	std::vector<Quad> quads; // Quad faces, if any.

	char materialName[materialNameMaxLen];
	bool drawable; // If this group is to be rendered or not. (by default true)

	// Default init:
	inline Group(void) : drawable(true) { memset(materialName, 0, materialNameMaxLen); }
};

// =========================================================
// Mesh Class
// =========================================================

class Mesh {

public:

	// Nested types:
	typedef std::map<const std::string, Group *> GroupMap;
	typedef std::map<const std::string, GfxLib::Material *> MaterialMap;

	// Public data:
	size_t numTriangles;
	size_t numQuads;

	size_t numVertices;
	size_t numNormals;
	size_t numTexCoords;

	MathLib::Vec3f * vertices;
	MathLib::Vec3f * normals;
	MathLib::Vec2f * texCoords;

	GroupMap polyGroups; // Groups of polygons.
	MaterialMap materials; // Map of materials indexed by name.

public:

	// Default constructor (set everything to null)
	inline Mesh(void) : numTriangles(0), numQuads(0),
	numVertices(0), numNormals(0), numTexCoords(0),
	vertices(0), normals(0), texCoords(0) { /**/ }

	// Automatic cleanup
	inline ~Mesh(void)
	{
		delete[] vertices;
		delete[] normals;
		delete[] texCoords;

		GroupMap::const_iterator GroupIndex = polyGroups.begin();
		GroupMap::const_iterator GroupEnd = polyGroups.end();

		while (GroupIndex != GroupEnd)
		{
			delete (GroupIndex->second);
			++GroupIndex;
		}
		polyGroups.clear();

		MaterialMap::const_iterator MatIndex = materials.begin();
		MaterialMap::const_iterator MatEnd = materials.end();

		while (MatIndex != MatEnd)
		{
			delete (MatIndex->second);
			++MatIndex;
		}
		materials.clear();
	}

private:

	// Disable copy and assignment.
	Mesh(const Mesh &);
	Mesh & operator = (const Mesh &);
};

// =========================================================
// Animated Mesh
// =========================================================

class AnimatedMesh {

public:

	// Will load all .obj files from the given directory and use them as keyframes.
	AnimatedMesh(const char * dirWithMeshes, int frameRate = 24);

	// Advance the animation frames.
	void Animate(float deltaTime);

	// Cleanup memory. Its good to have destructors :)
	~AnimatedMesh(void);

public:

	typedef std::vector<Mesh *> KeyFrameList;

	KeyFrameList keyFrames; // Animation key frames. Each frame is a mesh.
	float timeCount;  // Animation timer
	float maxTime;    // Frame duration
	int frameRate;    // Frame rate per second
	int currentFrame; // Current frame (mesh1)
	int nextFrame;    // Next frame (mesh2)
};

// =========================================================
// Mesh File Loading
// =========================================================

// Wavefront OBJ mesh loader.
bool MeshFromWavefrontObject(const char * fileName, Mesh * mesh);

}; // namespace MeshLib {}

#endif // __MESHLIB_HPP__